﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PJ.Data.Exception;
using PJ.Framework.Common;
using System.Collections.ObjectModel;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using System.Collections;

namespace PJ.Data
{
    /// <summary>
    /// ObjectPICProvider : Persist In Cache data provider
    /// </summary>
    /// <typeparam name="I">Object to provide interface type</typeparam>
    /// <typeparam name="C">Object to provide concrete class type</typeparam>
    public abstract class ObjectPICProvider<I, C> : IObjectProvider<I>, IDisposable
        where C : BaseObjectData, I, new()
        where I : class, IObjectData
    {
        private readonly ICacheManager m_Cache;

        private Dictionary<string, C> m_DataObjects;

        protected Dictionary<string, C> DataObjects
        {
            get 
            {
                if (m_DataObjects ==  null)
                {
                    m_DataObjects = m_Cache.GetData(this.GetType().Name) as Dictionary<string, C>;
                    if (m_DataObjects == null)
                        m_DataObjects = new Dictionary<string, C>();
                }
                return m_DataObjects;
            }
        }

        public ObjectPICProvider(ICacheManager cache)
        {
            if (cache == null)
                throw new ArgumentNullException();
            m_Cache = cache;
        }

        protected void Save()
        {
            if (m_DataObjects != null)
                m_Cache.Add(this.GetType().Name, DataObjects);
        }

        public virtual bool Exist(string globalId)
        {
            if (globalId == null)
                throw new ArgumentNullException();
            return (from d in DataObjects where d.Key == globalId select d).Count() > 0;
        }

        public virtual void Delete(string globalId)
        {
            if (globalId == null)
                throw new ArgumentNullException();
            DataObjects.Remove(globalId);
        }

        public virtual I GetData(string globalId, GetDataMode mode)
        {
            if (globalId == null)
                throw new ArgumentNullException();
            C obj = null;
            if (!DataObjects.TryGetValue(globalId, out obj))
                if ((mode & GetDataMode.Find) != GetDataMode.Find)
                    throw new DataNotFoundException("globalId", globalId);
                else
                    return null;
            else
                return obj as I;
        }

        public virtual List<I> GetList(string[] globalIds, GetDataMode mode)
        {
            if (globalIds == null || globalIds.Count() == 0)
                throw new ArgumentNullException();
            List<I> list = new List<I>();
            foreach (string globalId in globalIds)
            {
                C obj = null;
                if (DataObjects.TryGetValue(globalId, out obj))
                    list.Add((I)obj);
            }
            if (list.Count == 0)
                list = null;
            if ((list == null || list.Count != globalIds.Count()) && (mode & GetDataMode.Find) != GetDataMode.Find)
                throw new DataNotFoundException("globalIds", globalIds.ToString());
            return list;
        }



        private C Set(C entity)
        {
            if (entity == null)
                throw new ArgumentNullException();
            if (entity.GlobalId == null || entity.GlobalId == "")
                entity.GlobalId = Guid.NewGuid().ToString();
            else
            {
                C old = null;
                if (DataObjects.TryGetValue(entity.GlobalId, out old))
                    if (old != entity)
                        DataObjects.Remove(entity.GlobalId);
                    else
                        return entity;
            }
            DataObjects.Add(entity.GlobalId, entity);
            Save();
            return entity;
        }
        public virtual void Persist(I entity)
        {
            PersistAndGet(entity);
        }

        public virtual I PersistAndGet(I entity)
        {
            if (entity == null)
                throw new ArgumentNullException();
            C t = entity as C;
            if (t == null)
            {
                t = new C();
                t.Copy(entity);
            }
            return Set(t);
        }

        public virtual void Dispose()
        {
            if (m_DataObjects != null)
                Save();
        }

        public virtual I CreateNewItem()
        {
            return new C();
        }

        #region Interface override
        IObjectData IObjectProvider.CreateNewItem()
        {
            return CreateNewItem();
        }

        IObjectData IObjectProvider.GetData(string globalId, GetDataMode mode)
        {
            return GetData(globalId, mode);
        }

        IEnumerable IObjectProvider.GetList(string[] globalIds, GetDataMode mode)
        {
            return GetList(globalIds, mode);
        }

        public virtual IObjectData PersistAndGet(IObjectData data)
        {
            return PersistAndGet((I)data);
        }

        public virtual void Persist (IObjectData data)
        {
            Persist((I)data);
        }

        #endregion

    }
}
